#!/usr/bin/env python

from pysqlite2 import dbapi2 as sqlite ##imports required modules for SQLite implementation
import os
import urllib
import shutil
import sys
import subprocess
import time
from decimal import *

##Gets track information from SQLite database, removes troublesome characters and replaces them with a space
def getProperty(_cursor, _property_id, _current_id):
    value = ''
    cursor2.execute('SELECT obj FROM resource_properties WHERE media_item_id = '+ str(_current_id) + ' AND property_id = ' + str(_property_id))
    for temp in _cursor:
        value = temp[0]
    bad_chars =["@","/","*","\\","|","<",">",'"',"?",",",":"]
    for i in bad_chars:
        value = value.replace(i,' ')
    return value

##Gets the path for the Songbird database
def getSongbirdDB():
    exit_status = 0
    #os.system('zenity --info --title="Songbird database source" --text="Please select the Songbird database to consolidate."')
    proc = subprocess.Popen('zenity --info --title="Songbird database source" --text="Please select the Songbird database to consolidate."',shell=True)
    proc.wait()
    
    proc = subprocess.Popen('zenity --file-selection --title="Select a File"', 
                        shell=True, 
                        stdout=subprocess.PIPE,
                        )

    exit_status = proc.wait()

    songbird_db = proc.communicate()[0]
    songbird_db = songbird_db.strip()
    if exit_status == 1:
        raise SystemExit
    return songbird_db

##Gets the output directory - where the new artist and album folders will be created
def getDestinationDir():
    exit_status = 0
    os.system('zenity --question --title="Destination directory" --text="Please select the directory to where your music will be copied."')

    proc = subprocess.Popen('zenity --file-selection --directory --title="Select a Directory"', 
                            shell=True, 
                            stdout=subprocess.PIPE,
                            )
    exit_status = proc.wait()
    output_dir = proc.communicate()[0]
    output_dir = output_dir.strip()
    if exit_status == 1:
        raise SystemExit
    return output_dir

##Asks user if files should be moved (originals deleted) or copied (originals untouched)
def moveOrCopy():
    exit_status = 0
    _mv_cp = 1

    proc = subprocess.Popen('zenity --question --title="Delete source files?" --text="Pressing OK will remove the source music files once they are copied to the new directory.\nPressing cancel will continue with library consolidaton, but not remove the original source files."', 
                            shell=True )
    exit_status = proc.wait()
    if exit_status == 1:
        _mv_cp = 1
    return _mv_cp

##Displays info about consolidation upon completion or cancellation
def infoMessage(_cancelled, _num_entries, _errors,_skipped,_copied):
    info = 'zenity --info --title="Consolidation ' +_cancelled + '" --text="' + str(_copied) + ' of ' + str(_num_entries) + ' media items moved/copied to new directory.\n' + str(_skipped) + ' items were already in place and were skipped.\nThere were ' + str(_errors) + ' error(s). See logfile." --percentage=0'
    proc2 = subprocess.Popen(info, 
                            shell=True) 
    proc2.wait()

##variables used
song_artist = ''
album_title = ''
song_title = ''
song_tracknum = ''
album_artist = ''
current_file = ''
current_id = ''
output_dir = ''
errors = 0
songbird_db = ''
mv_cp = 1;
cancelled = "finished"
skipped = 0
copied = 0
percent = 0

# opens file for logging errors
logfile = open('errors.log', 'w')

##Gets Songbird DB
songbird_db = getSongbirdDB()

##Connects to SQLite database
connection = sqlite.connect(songbird_db)

##creates cursor objects which we use to interact with the connection object
cursor1 = connection.cursor()
cursor2 = connection.cursor()

##Executes query to determine size of table (is there another way to do this?)
try:
    cursor1.execute('SELECT media_item_id, content_url FROM media_items WHERE is_list = 0')
except:
    proc = subprocess.Popen('zenity --info --title="Songbird database error" --text="There was an error in reading the Songbird database you selected.\nPlease make sure you have selected the correct database."',shell=True)
    proc.wait()
    raise SystemExit
    
    
num_entries = len(cursor1.fetchall())
#print num_entries

##Sets up variables for progress bar
getcontext().prec = 28 ##this needs to be decreased according to library size for better performance - 28 is max (safest but presumably slowest)
hundred = Decimal('100')
dec_entries = Decimal(num_entries)
increments = hundred / dec_entries
progress_count = 0
i = 0

##Gets output directory
output_dir = getDestinationDir()

##Move or copy files?
mv_cp = moveOrCopy()

##Opens zenity progress bar - allows user to cancel consolidation    
proc = subprocess.Popen('zenity --progress --title="Consolidation Progress" --text="Current media libaray consolidation progress " --percentage=0 --auto-close', 
                            shell=True,
                            stdin=subprocess.PIPE)

##Executes query to run through list of all items in Songbird library
cursor1.execute('SELECT media_item_id, content_url FROM media_items WHERE is_list = 0') #executes SQLite query
for row in cursor1:
    current_id = row[0] #media_item_ID
    current_file = row[1] #file location
    #print current_file
    song_title = getProperty(cursor2,1,current_id).strip()
    #print song_title
    album_title = getProperty(cursor2,2,current_id).strip()
    #print album_title
    song_artist = getProperty(cursor2,3,current_id).strip()
    #print song_artist
    song_tracknum = getProperty(cursor2,6,current_id)
    if len(song_tracknum) < 2: ##places '0' prefix if track number is only one digit
            song_tracknum = "0" + song_tracknum
    album_artist = getProperty(cursor2,22,current_id).strip() #changed porperty id from 28 to 22 as album artist is now 22.
    album_artist = album_artist.lower()
    
    old_path = urllib.unquote(current_file[7:len(current_file)]) ##removes the '%20' stuff from the path
    #print old_path
    
    ##Generates artist path - checks if compilation
    if album_artist == "compilation":
        artist_path = output_dir + "/" + "Compilations"
        #print "is a compilation"
    elif album_artist == "soundtrack":
        artist_path = output_dir + "/" + "Soundtracks"
    else:
        artist_path = output_dir + "/" + song_artist
    
    ##Change this to suit taste    
    new_filename =  song_tracknum + ' - ' + song_artist + ' - ' + song_title
    
    ##If no album title use "Unknown Album"    
    if album_title == '':
        album_path = artist_path + "/" + "Unknown Album"
    else:
        album_path = artist_path + "/" + album_title
    
    ##Creates artist path 
    try:        
        if not os.path.exists(artist_path):
            os.mkdir(artist_path)
    except OSError:
        #print "OS Error!\n\tDir problem with artist path\n"
        logerror = "os error : " + artist_path + "\n"
        #print logerror
        logfile.write(logerror)
        errors += 1
        #raise
    
    ##Creates album path   
    try:   
        if not os.path.exists(album_path):
            os.mkdir(album_path)
    except OSError:
        #print "OS Error!\n\tDir problem with album path\n"
        logerror = "os error : " + artist_path  + "\n"
        #print logerror
        logfile.write(logerror)
        errors += 1
        

    ##New path for current file being consolidated
    new_path = album_path + '/' + new_filename + old_path[len(old_path)-4:len(old_path)]
    
    ##Somehow this fixes problems with encoding
    old_path = old_path.encode('latin-1')

    ##Checks if paths are the same (file already in correct location)
    if (not (os.path.exists(new_path) and os.path.exists(old_path))):
        try: ##Copies and renames old path to new path
            copied += 1
            shutil.copy(old_path,new_path)
            if mv_cp == 0: ##Checks if user wantes original deleted
                os.remove(old_path)
        except IOError:
            #print "file IO error!\n\tCurrent media item ID:", current_id, "\n\tPath: ",old_path,"\n"
            logerror = "file io error. media item id: "+ str(current_id) + " Path: " +old_path  + "\n"
            logfile.write(logerror)
            #raise
            errors += 1
    else: ## Paths exist, skip this track
        skipped += 1
                
    ##Checks exit status of progress bar
    exit_status = proc.poll()

    if exit_status == 1:
        cancelled = "cancelled"
        infoMessage(cancelled, num_entries, errors,skipped,copied)
        logfile.close()
        raise SystemExit
    else:
        progress_count += 1
        i = i + increments
        percent = round(i,4)
        proc.stdin.write('# Percent complete: %.3f %s\n' % (percent,'%'))
        proc.stdin.write('%d\n' % i)
        
 
##Closes logfile
logfile.close()
##Completion message
infoMessage(cancelled, num_entries, errors,skipped,copied)

            
